#include "quantizer.h"
#include <QByteArray>
#include <QtConcurrent/QtConcurrent>

namespace WingGif {

Quantizer::Quantizer(bool singlePass, QObject *parent) : QObject(parent) {
  _singlePass = singlePass;
}

int Quantizer::depth() { return _depth; }

void Quantizer::setDepth(int value) { _depth = value; }

int Quantizer::maxColors() { return _maxColors; }

bool Quantizer::setMaxColors(int value) {
  if (value < 2 || value > 256)
    return false;
  _maxColors = value;
  return true;
}

int Quantizer::maxColorsWithTransparency() {
  return _maxColorsWithTransparency;
}

void Quantizer::setMaxColorsWithTransparency(int value) {
  _maxColorsWithTransparency = value;
}

QVector<QColor> Quantizer::colorTable() { return _colorTable; }

void Quantizer::setColorTable(QVector<QColor> value) {
  _colorTable.swap(value);
}

QColor Quantizer::transparentColor() { return _transparentColor; }

void Quantizer::setTransparentColor(QColor value) { _transparentColor = value; }

uchar Quantizer::transparentColorIndex() {
  auto max = _transparentColor.isValid() ? _maxColors - 1 : _maxColors;
  Q_UNUSED(max);

  //?
  return 0;
}

QByteArray Quantizer::quantize(QByteArray pixels, bool secondPassOnly) {
  // When using a global color table, the analysis should not be executed again.
  if (!secondPassOnly) {
    if (!_singlePass)
      firstPass(pixels);
    _colorTable = buildPalette();
  }
  return secondPass(pixels);
}

void Quantizer::firstPass(QByteArray pixels) {
  for (auto i = 0; i < pixels.length(); i += _depth)
    initialQuantizePixel(
        QColor::fromRgba(qRgba(pixels[i + 2], pixels[i + 1], pixels[i],
                               pixels[i + 3]))); // Pixels are in BGR.
}

QVector<QColor> Quantizer::getPalette() { return _colorTable = buildPalette(); }

QByteArray Quantizer::parallelSecondPass(QByteArray pixels) {
  auto len = pixels.length() / _depth;
  auto output = reinterpret_cast<char *>(malloc(ulong(len)));
  if (!output)
    return QByteArray();
  QtConcurrent::run([=] {
    for (auto index = 0; index < len; index++) {
      auto trueIndex = index * _depth;
      // Transparent pixels translate to the end of the color table.
      if (pixels[trueIndex + 3] == 0) {
        output[index] = char(_colorTable.count() - 1);
        return;
      }
      auto pixel =
          QColor::fromRgba(qRgba(pixels[trueIndex + 2], pixels[trueIndex + 1],
                                 pixels[trueIndex], pixels[trueIndex + 3]));
      auto hash = int(pixel.rgb());
      if (_colorMap.contains(hash)) {
        output[index] = char(_colorMap[hash]);
        return;
      }
      auto position = quantizePixel(pixel);
      output[index] = char(position);
      _colorMap.insert(hash, position);
    }
  });
  auto ret = QByteArray(output);
  free(output);
  return ret;
}

QByteArray Quantizer::secondPass(QByteArray pixels) {
  QByteArray output;
  for (auto index = 0; index < pixels.length(); index += _depth) {
    // Transparent pixels translate to the end of the color table.
    if (pixels[index + 3] == char(0)) {
      output.append(char(_colorTable.count() - 1));
      continue;
    }
    auto pixel = QColor::fromRgba(qRgba(pixels[index + 2], pixels[index + 1],
                                        pixels[index], pixels[index + 3]));
    auto hash = int(pixel.rgb());
    if (_colorMap.contains(hash)) {
      output.append(char(_colorMap[hash]));
      continue;
    }
    auto position = quantizePixel(pixel);
    output.append(char(position));
    _colorMap.insert(hash, position);
  }
  return output;
}

} // namespace WingGif
